/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 - 2008 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.web.gwt.form.server.spring.factory;

import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.FactoryBeanNotInitializedException;

import com.genia.toolbox.basics.i18n.EnumDescriptable;
import com.genia.toolbox.web.gwt.basics.client.i18n.GwtI18nMessage;
import com.genia.toolbox.web.gwt.form.client.form.EnumDescriptableValuedItemEntryList;
import com.genia.toolbox.web.gwt.form.client.form.impl.EnumDescriptableValuedItemEntryListImpl;
import com.genia.toolbox.web.gwt.form.client.form.impl.ValuedItemEntryImpl;

/**
 * a factory to generate a {@link EnumDescriptableValuedItemEntryList} from the
 * class name of the enum.
 */
public class EnumDescriptableValuedItemEntryListFactory
    implements FactoryBean
{
  /**
   * the name of the class of the enumeration to use to generate this list.
   */
  private String enumClassName;

  /**
   * the constructed object.
   */
  private EnumDescriptableValuedItemEntryListImpl object = null;



  /**
   * Return an instance (possibly shared or independent) of the object managed
   * by this factory.
   * <p>
   * As with a {@link org.springframework.beans.factory.BeanFactory}, this
   * allows support for both the Singleton and Prototype design pattern.
   * <p>
   * If this FactoryBean is not fully initialized yet at the time of the call
   * (for example because it is involved in a circular reference), throw a
   * corresponding
   * {@link org.springframework.beans.factory.FactoryBeanNotInitializedException}.
   * <p>
   * As of Spring 2.0, FactoryBeans are allowed to return <code>null</code>
   * objects. The factory will consider this as normal value to be used; it will
   * not throw a
   * org.springframework.beans.factory.FactoryBeanNotInitializedException in
   * this case anymore. FactoryBean implementations are encouraged to throw
   * org.springframework.beans.factory.FactoryBeanNotInitializedException
   * themselves now, as appropriate.
   * 
   * @return an instance of the bean (can be <code>null</code>)
   * @see org.springframework.beans.factory.FactoryBeanNotInitializedException
   * @see org.springframework.beans.factory.ObjectFactory#getObject()
   */
  @SuppressWarnings("unchecked")
  public Object getObject()
  {
    if (object == null) {
      object = new EnumDescriptableValuedItemEntryListImpl();
      object.setEnumClassName(getEnumClassName());
      Class<? extends Enum<?>> enumClass = null;
      try {
        enumClass = (Class<? extends Enum<?>>) Class.forName(getEnumClassName());
      }
      catch (Exception e) {
        throw new FactoryBeanNotInitializedException("Unable to generate class for: " + getEnumClassName());
      }
      for (Enum<?> enumValue : enumClass.getEnumConstants()) {
        EnumDescriptable enumDescriptable = null;
        try {
          enumDescriptable = (EnumDescriptable) enumValue;
        }
        catch (ClassCastException e) {
          throw new FactoryBeanNotInitializedException("Class " + getEnumClassName() + " is not of type EnumDescriptable");
        }
        ValuedItemEntryImpl vie = new ValuedItemEntryImpl();
        vie.setLabel(new GwtI18nMessage(enumDescriptable.getDescription().getKey()));
        vie.setValue(String.valueOf(enumValue.ordinal()));
        object.getValuedItemEntries().add(vie);
      }
    }
    return object;
  }



  /**
   * Return the type of object that this FactoryBean creates, or
   * <code>null</code> if not known in advance.
   * <p>
   * This allows one to check for specific types of beans without instantiating
   * objects, for example on autowiring.
   * <p>
   * In the case of implementations that are creating a singleton object, this
   * method should try to avoid singleton creation as far as possible; it should
   * rather estimate the type in advance. For prototypes, returning a meaningful
   * type here is advisable too.
   * <p>
   * This method can be called <i>before</i> this FactoryBean has been fully
   * initialized. It must not rely on state created during initialization; of
   * course, it can still use such state if available.
   * <p>
   * <b>NOTE:</b> Autowiring will simply ignore FactoryBeans that return
   * <code>null</code> here. Therefore it is highly recommended to implement
   * this method properly, using the current state of the FactoryBean.
   * 
   * @return the type of object that this FactoryBean creates, or
   *         <code>null</code> if not known at the time of the call
   * @see org.springframework.beans.factory.ListableBeanFactory#getBeansOfType
   * @see org.springframework.beans.factory.FactoryBean#getObjectType()
   */
  public Class<EnumDescriptableValuedItemEntryList> getObjectType()
  {
    return EnumDescriptableValuedItemEntryList.class;
  }



  /**
   * Is the object managed by this factory a singleton? That is, will
   * {@link #getObject()} always return the same object (a reference that can be
   * cached)?
   * <p>
   * <b>NOTE:</b> If a FactoryBean indicates to hold a singleton object, the
   * object returned from <code>getObject()</code> might get cached by the
   * owning BeanFactory. Hence, do not return <code>true</code> unless the
   * FactoryBean always exposes the same reference.
   * <p>
   * The singleton status of the FactoryBean itself will generally be provided
   * by the owning BeanFactory; usually, it has to be defined as singleton
   * there.
   * <p>
   * <b>NOTE:</b> This method returning <code>false</code> does not
   * necessarily indicate that returned objects are independent instances. An
   * implementation of the extended
   * {@link org.springframework.beans.factory.SmartFactoryBean} interface may
   * explicitly indicate independent instances through its
   * {@link org.springframework.beans.factory.SmartFactoryBean#isPrototype()}
   * method. Plain {@link FactoryBean} implementations which do not implement
   * this extended interface are simply assumed to always return independent
   * instances if the <code>isSingleton()</code> implementation returns
   * <code>false</code>.
   * 
   * @return if the exposed object is a singleton
   * @see #getObject()
   * @see org.springframework.beans.factory.SmartFactoryBean#isPrototype()
   * @see org.springframework.beans.factory.FactoryBean#isSingleton()
   */
  public boolean isSingleton()
  {
    return true;
  }



  /**
   * getter for the enumClassName property.
   * 
   * @return the enumClassName
   */
  public String getEnumClassName()
  {
    return enumClassName;
  }



  /**
   * setter for the enumClassName property.
   * 
   * @param enumClassName
   *          the enumClassName to set
   */
  public void setEnumClassName(String enumClassName)
  {
    this.enumClassName = enumClassName;
  }

}
